package solver

import "slices"

func merge(r [][]int, x int) [][]int {
	if len(r) == 0 {
		return [][]int{{x}}
	}
	for i, xs := range r {
		r[i] = append([]int{x}, xs...)
	}
	return r
}

func combine(xs []int, n int) [][]int {
	if n < 1 {
		return [][]int{}
	}
	if n == 1 {
		r := [][]int{}
		for _, x := range xs {
			r = append(r, []int{x})
		}
		return r
	}
	length := len(xs)
	if length < n {
		return [][]int{}
	}
	if length == n {
		return [][]int{xs}
	}
	return append(merge(combine(xs[1:], n-1), xs[0]), combine(xs[1:], n)...)
}

func calcBoxAndCellNum(rowNum int, colNum int) (boxNum int, cellNum int) {
	boxNum = rowNum/3*3 + colNum/3
	cellNum = rowNum%3*3 + colNum%3
	return
}

func calcRowAndColNum(boxNum int, cellNum int) (rowNum int, colNum int) {
	rowNum = boxNum/3*3 + cellNum/3
	colNum = boxNum%3*3 + cellNum%3
	return
}

func (c *Cell) set(value int) {
	c.value = value
	if value == 0 {
		c.candidateCount = 9
		c.candidates = [9]bool{true, true, true, true, true, true, true, true, true}
	} else {
		c.candidateCount = 1
		c.candidates = [9]bool{}
		c.candidates[value-1] = true
	}
}

func (c *Cell) removeCandidateBut(vs []int) (used bool) {
	if c.value != 0 {
		return false
	}
	for i := 0; i < 9; i++ {
		if slices.Contains(vs, i) || !c.candidates[i] {
			continue
		}
		c.candidates[i] = false
		c.candidateCount--
		used = true
	}
	return
}

func (c *Cell) removeCandidate(value int) bool {
	if c.candidateCount == 1 || !c.candidates[value-1] {
		return false
	}
	c.candidateCount -= 1
	c.candidates[value-1] = false
	return true
}

func (g *Grid) sumup(house House) (candFreqs [9]int, cellFreqs [9]int, candCells [9][9]bool) {
	for i, cell := range house.cells {
		if cell.value != 0 {
			continue
		}
		for j, candidate := range cell.candidates {
			if candidate {
				candCells[j][i] = true
				cellFreqs[i]++
				candFreqs[j]++
			}
		}
	}

	return
}

func sumupValueCells(house *House) (valueCells [9][9]bool, valueCounts [9]int) {
	for ci, cell := range house.cells {
		if cell.value != 0 {
			continue
		}
		for vi, candidate := range cell.candidates {
			if candidate {
				valueCells[vi][ci] = candidate
				valueCounts[vi]++
			}
		}
	}
	return
}

func cellNumsForNaked(house *House, nakedCount int) []int {
	cellNums := []int{}
	for cellNum, cell := range house.cells {
		if cell.value != 0 || cell.candidateCount > nakedCount {
			continue
		}
		cellNums = append(cellNums, cellNum)
	}
	return cellNums
}

func clearupNakedHouse(house *House, cellNums []int, values []int) (used bool) {
	for i, cell := range house.cells {
		if cell.value != 0 || slices.Contains(cellNums, i) {
			continue
		}
		for _, value := range values {
			if cell.removeCandidate(value) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) doStrategy(doStrategyHouse func(*House) bool) (used bool) {
	for _, box := range g.boxes {
		if doStrategyHouse(&box) {
			used = true
		}
	}

	for _, row := range g.rows {
		if doStrategyHouse(&row) {
			used = true
		}
	}

	for _, col := range g.cols {
		if doStrategyHouse(&col) {
			used = true
		}
	}
	return
}
